//
// (C) Copyright 2006 Alexander Tsvyashchenko - http://www.ndl.kiev.ua
//
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_PP_IS_ITERATING

# ifndef FASTSIG_ITER_HPP
# define FASTSIG_ITER_HPP

# ifndef FASTSIG_MAX_ARGS
# define FASTSIG_MAX_ARGS 8  // default maximum number of arguments is 8
# endif

# include <boost/shared_ptr.hpp>
# include <boost/type_traits/add_pointer.hpp>
# include <list>

// generate specializations
# include <boost/preprocessor/iteration/iterate.hpp>
# include <boost/preprocessor/repetition.hpp>
# include <boost/preprocessor/punctuation/comma_if.hpp>

# define BOOST_PP_ITERATION_LIMITS (0, FASTSIG_MAX_ARGS)
# define BOOST_PP_FILENAME_1 "fastsig_iter.h" // this file
# include BOOST_PP_ITERATE()

namespace fastsig
  {
  template <class Signature>
  class signal: public detail::signal_impl<typename boost::add_pointer<Signature>::type>
    {
    };
  }

# endif // FASTSIG_ITER_HPP

#else // BOOST_PP_IS_ITERATING
//-----------------------------------------------------------------------------
# define n BOOST_PP_ITERATION()
//-----------------------------------------------------------------------------
namespace fastsig
  {
  namespace detail
    {
    template <class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    class slot_base<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>
      {
      typedef R (*Signature)(BOOST_PP_ENUM_PARAMS(n,arg));

      public:
        virtual R emit(BOOST_PP_ENUM_PARAMS(n, arg)) const = 0;
        virtual R operator()(BOOST_PP_ENUM_PARAMS(n, arg)) const = 0;
        virtual ~slot_base() {}
      };

    template <class Slot,
              class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    class slot_impl<Slot, R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>: public slot_base<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>
      {
      public:
        slot_impl(const Slot& slot): m_slot(slot) {}

        virtual R emit(BOOST_PP_ENUM_PARAMS(n, arg)) const;
        virtual R operator()(BOOST_PP_ENUM_PARAMS(n, arg)) const;

      private:
        mutable Slot m_slot;
      };

    template <class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    class signal_impl<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>: public signal_impl_common<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>
      {
      typedef R (*Signature)(BOOST_PP_ENUM_PARAMS(n,arg));

      public:
        R emit(BOOST_PP_ENUM_PARAMS(n, arg)) const;
        R operator()(BOOST_PP_ENUM_PARAMS(n, arg)) const;
      };

    template <BOOST_PP_ENUM_PARAMS(n,class arg)>
    class signal_impl<void (*)(BOOST_PP_ENUM_PARAMS(n,arg))>: public signal_impl_common<void (*)(BOOST_PP_ENUM_PARAMS(n,arg))>
      {
      typedef void (*Signature)(BOOST_PP_ENUM_PARAMS(n,arg));

      public:
        void emit(BOOST_PP_ENUM_PARAMS(n, arg)) const;
        void operator()(BOOST_PP_ENUM_PARAMS(n, arg)) const;
      };
    } // namespace detail
  } // namespace fastsig
//-----------------------------------------------------------------------------
// Implementation
//-----------------------------------------------------------------------------
namespace fastsig
  {
  namespace detail
    {
    template <class Slot,
              class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    inline
    R slot_impl<Slot, R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::emit(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      return m_slot(BOOST_PP_ENUM_PARAMS(n,a));
      }

    template <class Slot,
              class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    inline
    R slot_impl<Slot, R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::operator ()(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      return m_slot(BOOST_PP_ENUM_PARAMS(n,a));
      }

    template <class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    inline
    R detail::signal_impl<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::emit(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      typedef detail::signal_impl_common<Signature> signal;
          iterating=true;//lock
      R ret_val;
      for (/*typename signal::slots::const_iterator*/ it = signal::m_slots.begin();
           it != signal::m_slots.end(); /*++it*/)
          {
        ret_val = it -> first -> emit(BOOST_PP_ENUM_PARAMS(n,a));
                (this->*advanceIterator)();
          }
          iterating=false;//release lock
      return ret_val;
      }

    template <class R
              BOOST_PP_COMMA_IF(n)
              BOOST_PP_ENUM_PARAMS(n,class arg)>
    inline
    R detail::signal_impl<R (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::operator ()(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      return emit(BOOST_PP_ENUM_PARAMS(n,a));
      }

  // According to the standard, template <> is not allowed here? (VS.NET 2005: Compiler Error C2910)
  #if n > 0
    template <BOOST_PP_ENUM_PARAMS(n,class arg)>
  #endif
    inline
    void detail::signal_impl<void (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::emit(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      typedef detail::signal_impl_common<Signature> signal;
          iterating=true;//lock

      for (
  // G++ does not allow typename here in the case n == 0
  /*#if n > 0
        typename
  #endif
        signal::slots::const_iterator*/ it = signal::m_slots.begin();
           it != signal::m_slots.end(); /*++it*/)
                   {
                        it -> first -> emit(BOOST_PP_ENUM_PARAMS(n,a));
                        (this->*advanceIterator)();
                   }
                   iterating=false;//release lock
      }

  #if n > 0
    template <BOOST_PP_ENUM_PARAMS(n,class arg)>
  #endif
    inline
    void detail::signal_impl<void (*)(BOOST_PP_ENUM_PARAMS(n,arg))>::operator ()(BOOST_PP_ENUM_BINARY_PARAMS(n,arg,a)) const
      {
      return emit(BOOST_PP_ENUM_PARAMS(n,a));
      }
    }
  } // namespace fastsig
//-----------------------------------------------------------------------------

#endif // BOOST_PP_IS_ITERATING
